package com.easyadmin.pro.common.aspect.log;

import com.easyadmin.pro.common.annotation.log.Log;
import com.easyadmin.pro.common.enums.HttpCodeEnum;
import com.easyadmin.pro.common.exception.BusinessException;
import com.easyadmin.pro.tool.IpUtils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Objects;

/**
 * packageName com.easyadmin.pro.common.aspect.log
 *
 * @author 骑着蚂蚁去上天
 * @version JDK 17
 * @className LogAspect
 * @date 2024/9/3
 * @description 日志切面
 */
@Slf4j
@Aspect
@Order(3)
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class LogAspect {

    private final HttpServletRequest request;

    private final Class<?>[] primitiveType = {int.class, long.class, short.class,
            byte.class, float.class, double.class, char.class, boolean.class, Serializable.class, String.class};


    @Pointcut("@annotation(com.easyadmin.pro.common.annotation.log.Log)")
    public void pointcut() {

    }

    @Around("pointcut()")
    public Object interceptor(ProceedingJoinPoint joinPoint) {
        try {
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            Method method = methodSignature.getMethod();
            Log logAnnotation = method.getAnnotation(Log.class);
            request.getMethod();
            if (Objects.nonNull(logAnnotation)) {
                // 方法名称
                String methodName = logAnnotation.methodName();
                // 请求类型
                String reqType = request.getMethod();
                // 请求参数
                String reqParams = getParamValueString(method.getParameters(), joinPoint.getArgs());
                // 获取IP地址
                String ipAddr = IpUtils.getIpAddr(request);
                // 实现日志处理业务逻辑
            }
            // 继续执行方法
            return joinPoint.proceed();
        } catch (Throwable e) {
            throw new BusinessException(HttpCodeEnum.SYSTEM_ERROR);
        }
    }

    /**
     * 获取参数信息
     * @param parameters
     * @param args
     * @return
     */
    private String getParamValueString(Parameter[] parameters, Object[] args) throws IllegalAccessException {
        StringBuilder sb = new StringBuilder("{");
        int num = 0;
        for (Parameter parameter : parameters) {
            Class<?> type = parameter.getType();
            Field[] fields = type.getDeclaredFields();
            Object obj = args[num];
            if (isNotPrimitiveType(obj)) {
                if (Objects.nonNull(obj)) {
                    for (Field field : fields) {
                        field.setAccessible(Boolean.TRUE);
                        String fieldName = field.getName();
                        Object value = field.get(obj);
                        if (sb.length() > 1) {
                            sb.append(",").append(fieldName).append(":").append(value);
                        } else {
                            sb.append(fieldName).append(":").append(value);
                        }
                    }
                }
            } else {
                if (sb.length() > 1) {
                    sb.append(",").append(parameter.getName()).append(":").append(obj);
                } else {
                    sb.append(parameter.getName()).append(":").append(obj);
                }
            }
            num++;
        }
        sb.append("}");
        return sb.toString();
    }

    /**
     * 判断是否是基本类型
     * @param obj
     * @return
     */
    private boolean isNotPrimitiveType(Object obj) {
        for (Class<?> aClass : primitiveType) {
            if (Objects.equals(obj.getClass(), aClass)) {
                return false;
            }
        }
        return true;
    }
}